package ${basepackage}.framework.base.dao;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javacommon.xsqlbuilder.SafeSqlProcesser;
import javacommon.xsqlbuilder.SafeSqlProcesserFactory;
import javacommon.xsqlbuilder.XsqlBuilder;
import javacommon.xsqlbuilder.XsqlBuilder.XsqlFilterResult;
import javacommon.xsqlbuilder.safesql.DirectReturnSafeSqlProcesser;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import ${basepackage}.framework.base.ColumnQuery;
import ${basepackage}.framework.base.page.BaseQuery;
import ${basepackage}.framework.base.page.Page;
import ${basepackage}.framework.base.page.PageRequest;
<#include "/author.include">
public abstract class BaseHibernateDao<E,PK extends Serializable> implements EntityDao<E,PK>{
	
	@Autowired 
    @Qualifier("sessionFactory")  
	private SessionFactory sessionFactory; 
	 
	 public Session getSession() {  
	        //事务必须是开启的，否则获取不到  
		    
		 Session session = sessionFactory.getCurrentSession();
	        
	        return session;
	    } 
	 
	 
	 
	/**
	 * Logger for subclass
	 */
	protected Log log = LogFactory.getLog(getClass());
	
	public long queryForLong(final String queryString) {
		return queryForLong(queryString,new Object[]{});
	}
	
	public long queryForLong(final String queryString,Object value) {
		return queryForLong(queryString,new Object[]{value});
	}
	
	public long queryForLong(final String queryString,Object[] values) {
		Query query = getSession().createQuery(queryString);
		if(values != null){
			for (int i = 0; i < values.length; i++)
            {
                query.setParameter(i, values[i]);
            }
		}
		return DataAccessUtils.longResult(query.list());
	}
	
	/**
	 * �õ�ȫ�����,��ִ�з�ҳ
	 * @param pageRequest
	 * @return
	 */
	public Page findAll(final PageRequest pageRequest) {
/*		return (Page)getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				
				StringBuffer queryString = new StringBuffer(" FROM ").append(getEntityClass().getSimpleName());
				String countQueryString = "SELECT count(*) " + queryString.toString();
				if(StringUtils.hasText(pageRequest.getSortColumns())) {
					queryString.append(" ORDER BY "+pageRequest.getSortColumns());
				}
				
				Query query = session.createQuery(queryString.toString());
				Query countQuery = session.createQuery(countQueryString);
				return PageQueryUtils.executeQueryForPage(pageRequest, query, countQuery);
			}
		});
*/	
		
		StringBuffer queryString = new StringBuffer(" FROM ").append(getEntityClass().getSimpleName());
		String countQueryString = "SELECT count(*) " + queryString.toString();
		if(StringUtils.hasText(pageRequest.getSortColumns())) {
			queryString.append(" ORDER BY "+pageRequest.getSortColumns());
		}
		
		Query query = getSession().createQuery(queryString.toString());
		Query countQuery = getSession().createQuery(countQueryString);
		return (Page) PageQueryUtils.executeQueryForPage(pageRequest, query, countQuery);
		
	}
	
	public Page pageQuery(final String sql,final PageRequest pageRequest) {
		final String countQuery = "select count(*) " + SqlRemoveUtils.removeSelect(SqlRemoveUtils.removeFetchKeyword((sql)));
		return pageQuery(sql,countQuery,pageRequest);
	}

	public Page pageQuery(final String sql,String countQuery,final PageRequest pageRequest) {
		Map otherFilters = new HashMap(1);
		otherFilters.put("sortColumns", pageRequest.getSortColumns());
		
		XsqlBuilder builder = getXsqlBuilder();
		
		//���ʹ��otherFilters��pageRequestΪһ��filtersʹ��
		XsqlFilterResult queryXsqlResult = builder.generateHql(sql,otherFilters,pageRequest);
		XsqlFilterResult countQueryXsqlResult = builder.generateHql(countQuery,otherFilters,pageRequest);
		
		return PageQueryUtils.pageQuery(getSession(),pageRequest,queryXsqlResult,countQueryXsqlResult);
	}
	
	public XsqlBuilder getXsqlBuilder() {
		
		SessionFactoryImpl sf = (SessionFactoryImpl)(getSession().getSessionFactory());
		Dialect dialect = sf.getDialect();
		
		//or SafeSqlProcesserFactory.getMysql();
		SafeSqlProcesser safeSqlProcesser = SafeSqlProcesserFactory.getFromCacheByHibernateDialect(dialect); 
		XsqlBuilder builder = new XsqlBuilder(safeSqlProcesser);
		
		if(builder.getSafeSqlProcesser().getClass() == DirectReturnSafeSqlProcesser.class) {
			System.err.println(BaseHibernateDao.class.getSimpleName()+".getXsqlBuilder(): ���ⱨ��,��δ����Sql��ȫ����,����ŵ�ת���ַ���ƴ��sqlʱ��Ҫת��,��Ȼ�ᵼ��Sqlע�빥���İ�ȫ���⣬���޸�Դ��ʹ��new XsqlBuilder(SafeSqlProcesserFactory.getDataBaseName())������ȫ����");
		}
		return builder;
	}
	
	static class PageQueryUtils {
		private static Page pageQuery(Session session,final PageRequest pageRequest, final XsqlFilterResult queryXsqlResult, final XsqlFilterResult countQueryXsqlResult) {
			System.out.println("queryXsqlResult.getXsql() = " + queryXsqlResult.getXsql());
			Query query = setQueryParameters(session.createQuery(queryXsqlResult.getXsql()),pageRequest);
			Query countQuery = setQueryParameters(session.createQuery(SqlRemoveUtils.removeOrders(countQueryXsqlResult.getXsql())),pageRequest);
			
			return (Page) executeQueryForPage(pageRequest, query, countQuery);
			
			
/*			return (Page)template.execute(new HibernateCallback() {
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					
					Query query = setQueryParameters(session.createQuery(queryXsqlResult.getXsql()),pageRequest);
					Query countQuery = setQueryParameters(session.createQuery(removeOrders(countQueryXsqlResult.getXsql())),pageRequest);
					
					return executeQueryForPage(pageRequest, query, countQuery);
				}
			});
*/
			
			}
		
		private static Object executeQueryForPage(final PageRequest pageRequest,Query query, Query countQuery) {
			Page page = new Page(pageRequest,((Number)countQuery.uniqueResult()).intValue());
			if(page.getTotalCount() <= 0) {
				page.setResult(new ArrayList(0));
			}else {
				page.setResult(query.setFirstResult(page.getFirstResult()).setMaxResults(page.getPageSize()).list());
			}
			return page;
		}
	
		public static Query setQueryParameters(Query q,Object params) {
			q.setProperties(params);
			return q;
		}
		
		public static Query setQueryParameters(Query q,Map params) {
			q.setProperties(params);
			return q;
		}
	}
	 
	public void save(E entity) {
		getSession().save(entity);
	}

	public List<E> findAll() {
		return getSession().createCriteria(getEntityClass()).list();
	}

	public E getById(PK id) {
		return (E)getSession().get(getEntityClass(),id);
	}

	public void delete(Object entity) {
		getSession().delete(entity);
	}
	
	public void delete(Serializable entity) {
		getSession().delete(entity);
	}
	
	public void deleteById(PK id) {
		Object entity = getById(id);
		if(entity == null) {
			throw new ObjectRetrievalFailureException(getEntityClass(),id);
		}
		getSession().delete(entity);
	}

	public void update(E entity) {
		getSession().update(entity);
	}

	public void saveOrUpdate(E entity) {
		getSession().saveOrUpdate(entity);
	}

	public void refresh(Object entity) {
		getSession().refresh(entity);
	}

	public void flush() {
		getSession().flush();
	}

	public void evict(Object entity) {
		getSession().evict(entity);
	}

	public void saveAll(Collection<E> entities) {
		for(Iterator<E> it = entities.iterator(); it.hasNext();) {
			save(it.next());
		}
	}

	public void deleteAll(Collection entities) {
		for(Object entity : entities) {
            this.getSession().delete(entity);
        }
	}

    public E findByProperty(final String propertyName, final Object value){
    	
    	/*        return (E)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return session.createCriteria(getEntityClass())
					.add(Expression.eq(propertyName,value))
					.uniqueResult();
			}
        });
*/
    	
    	return (E) getSession().createCriteria(getEntityClass()).add(Expression.eq(propertyName, value)).uniqueResult();

        }

    public List<E> findAllBy(final String propertyName, final Object value) {
/*        return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return session.createCriteria(getEntityClass())
					.add(Expression.eq(propertyName,value))
					.list();
			}
        });
*/
    	
    	
    	
    	return getSession().createCriteria(getEntityClass())
				.add(Expression.eq(propertyName,value))
				.list();
    }

    /**
	 * 判断对象某些属性的值在数据库中是否唯一.
	 *
	 * @param uniquePropertyNames 在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 */
	public boolean isUnique(E entity, String uniquePropertyNames) {
		Assert.hasText(uniquePropertyNames);
		Criteria criteria = getSession().createCriteria(getEntityClass()).setProjection(Projections.rowCount());
		String[] nameList = uniquePropertyNames.split(",");
		try {
			// 循环加入唯一列
			for (int i = 0; i < nameList.length; i++) {
				criteria.add(Restrictions.eq(nameList[i], PropertyUtils.getProperty(entity, nameList[i])));
			}

			// 以下代码为了如果是update的情况,排除entity自身.

			String idName = sessionFactory.getClassMetadata(entity.getClass()).getIdentifierPropertyName();
			if(idName != null) {
				// 取得entity的主键值
				Serializable id =  (Serializable)PropertyUtils.getProperty(entity, idName);
	
				// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
				if (id != null)
					criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
			}
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}
		return ((Number) criteria.uniqueResult()).intValue() == 0;
	}
	
    public Class getEntityClass(){
    	 Class c = getClass();
	        Type t = c.getGenericSuperclass();
	        if (t instanceof ParameterizedType) {
	            Type[] p = ((ParameterizedType) t).getActualTypeArguments();
	            return (Class<E>) p[0];
	        }
	        
	        return null;
    }
    
	@Override
	public Page findPage(BaseQuery baseQuery) {
		
		BaseQuery clone = (BaseQuery)baseQuery.clone();
		StringBuilder sb = new StringBuilder();
		sb.append("select t from " + getEntityClass().getSimpleName() + " t where 1 = 1 ");
		
		PropertyDescriptor[] pds;
		try {
			pds = Introspector.getBeanInfo(baseQuery.getClass()).getPropertyDescriptors();
			if(pds!=null){
				for(PropertyDescriptor pd:pds){
					
					Method readMethod = pd.getReadMethod();
					ColumnQuery c = readMethod.getAnnotation(ColumnQuery.class);
					
					if(c != null){
						String column = c.name();
						if(column == null || column.equals("")){
							column = pd.getName();
						}
						if("like".equalsIgnoreCase(c.operator())){
							try {
								pd.getWriteMethod().invoke(clone, "%" + pd.getValue(pd.getName()) + "%");
							} catch (Exception e) {
								e.printStackTrace();
							}
							sb.append("/~ and t." + column + " " + c.operator() + " '%[" + pd.getName() + "]%' ~/ ");
							
						}else
						{
							sb.append("/~ and t." + column + " " + c.operator() + " {" + pd.getName() + "} ~/ ");
						}
					}
				}
			}
			
			return pageQuery(sb.toString(),baseQuery);
		} catch (IntrospectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		
		

	}

}
